/*
    backend for TimeTagger, an OpalKelly based single photon counting library
    Copyright (C) 2011  Markus Wick <wickmarkus@web.de>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <assert.h>
#include <string>
#include <sstream>

#include "Counter.h"
#include "Logger.h"
DECLARE_LOG_SUPPORT(MODULES)

Counter::Counter(int _chan, long long _binwidth, int _bins) {
	debug("Counter created.");

	chan = _chan;
	binwidth = _binwidth;
	bins = _bins;

	data = new int[bins + 1];

	clear();

	registerChannel(chan);

	start();
}

Counter::~Counter() {
	stop();
	delete [] data;
}

const char * Counter::getScpiCommand() {
	return ":CONFigure:COUNTer";
}

std::string Counter::getScpiParameters() {
	std::stringstream ss;
	ss << "chan"<< chan << ", (" << binwidth << ',' << bins << ')';
	return ss.str();
}

const char *Counter::getClassName() {
	return "Counter";
}

void Counter::getMeta(long long **ARGOUTVIEWM_ARRAY2, int *DIM1) {
	const int sz=5;
	long long *arr = new long long[sz];

	synchronized(*this) {
		long long *ptr=arr;
		*ptr++=chan;
		*ptr++=binwidth;
		*ptr++=bins;
		*ptr++=starttime;
		*ptr++=pos;
	}

	*ARGOUTVIEWM_ARRAY2 = arr;
	*DIM1 = sz;
}

void Counter::clear() {
	lock();

	pos = 0;
	starttime = -1;

	for(int i=0; i<bins+1; i++)
		data[i] = 0;

	unlock();
}

void Counter::getData(int **ARGOUTVIEWM_ARRAY1, int *DIM1) {
	assert(data);
	int *arr = new int[bins];
	assert(arr);

	lock();

	for ( int i=0; i<bins; i++) {
		arr[i] = data[(i + pos + 1) % (bins+1)];
	}

	*ARGOUTVIEWM_ARRAY1 = arr;
	*DIM1 = bins;

	unlock();
}


void Counter::next(Tag* list, int count, long long time) {
	for(int i=0; i<count; i++) {
		if(list[i].overflow) {
			starttime = -1;
			pos = 0;
		} else if(list[i].chan == chan) {
			// erster durchlauf nach clean
			if(starttime == -1) starttime = list[i].time;

			// lange kein paket mehr gesehen
			if(list[i].time >= starttime + binwidth*(bins+1)) {
				pos = 0;
				starttime = list[i].time;
				
				for(int i=0; i<bins+1; i++)
					data[i] = 0;
			}

			// nicht mehr im aktuellen bin
			while(list[i].time >= starttime + binwidth) {
				starttime += binwidth;
				pos = (pos+1)%(bins+1);
				data[pos] = 0;
			}
			
			data[pos]++;
		}
	}
	// lange kein paket mehr gesehen
	if(time >= starttime + binwidth*(bins+1)) {
		pos = 0;
		starttime = time;

		for(int i=0; i<bins+1; i++)
			data[i] = 0;
	}
}



Countrate::Countrate(int _chan) {
	debug("Countrate created.");
	chan = _chan;
	
	clear();

	registerChannel(chan);
	start();
}

Countrate::~Countrate() {
	stop();
}

const char *Countrate::getClassName() {
	return "Countrate";
}

const char * Countrate::getScpiCommand() {
	return ":CONFigure:COUNTRate";
}

std::string Countrate::getScpiParameters() {
	std::stringstream ss;
	ss << "chan"<< chan;
	return ss.str();
}

void Countrate::getMeta(long long **ARGOUTVIEWM_ARRAY2, int *DIM1) {
	const int sz=4;
	long long *arr = new long long[sz];

	synchronized(*this) {
		long long *ptr=arr;
		*ptr++=chan;
		*ptr++=startzeit;
		*ptr++=now;
		*ptr++=flanken;
	}

	*ARGOUTVIEWM_ARRAY2 = arr;
	*DIM1 = sz;
}
double Countrate::getData() {
	lock();
	double d;
	if(now == startzeit)
		d = 0;
	else
		d = 1.0e12*double(flanken)/double(now-startzeit);
	unlock();
	return d;
}

void Countrate::clear() {
	lock();
	flanken = -1;
	startzeit = 0;
	now = 0;
	unlock();
}

void Countrate::next(Tag* list, int count, long long time) {
	for(int i=0; i<count; i++) {
		if(list[i].overflow) {
			startzeit = 0;
			flanken = 0;
		} else if(list[i].chan == chan) {
			if(!startzeit)
				startzeit = list[i].time;
			flanken++;
		}
	}
	now = time;
	if(!startzeit) startzeit = time;
}
